import Fluent
import Foundation
import Vapor

struct SessionService: ContentProtocol {
    typealias Answer = SessionModel
    typealias Request = Vapor.Request
    typealias CreateDTO = CreateSessionDTO
    typealias UpdateDTO = UpdateSessionDTO
    typealias Status = HTTPStatus
    typealias Model = SessionModel
    
    static func create(_ req: Request, createDTO: CreateDTO, author: UserModel) async throws -> Vapor.HTTPStatus
    {
        guard
            let course = try await CourseModel.find(createDTO.course, on: req.db)
        else {
            throw Abort(.notFound, reason: "Course not found")
        }
        let session: SessionModel = SessionModel()
        session.title = createDTO.title
        session.mp4URL = createDTO.mp4URL
        session.hlsURL = createDTO.hlsURL
        session.createdAt = Date()
        session.updatedAt = Date()
        session.publishedAt = createDTO.publishedAt
        session.price = createDTO.price ?? course.price
        session.article = createDTO.article
        session.course = createDTO.course
        session.status = createDTO.status
        session.slug = createDTO.title?.lowercased().replacingOccurrences(of: " ", with: "-")
        
        try await session.save(on: req.db)
        return .created
    }
    
    static func get(_ req: Request, object: String) async throws -> SessionModel {
        guard
            let session = try await SessionModel.query(on: req.db)
                .filter(\.$slug == object)
                .first()
        else {
            throw Abort(.notFound, reason: "Session not found")
        }
        return session
    }
    
    static func getAll(_ req: Request) async throws -> [SessionModel] {
        let sessions = try await SessionModel.query(on: req.db).all()
        return sessions
    }
    
    static func update(_ req: Request, object: String, updateDTO: UpdateDTO) async throws -> SessionModel {
        let uuid = try await getIDFromSlug(req, slug: object)
        guard
            let session = try await SessionModel.find(uuid, on: req.db)
        else {
            throw Abort(.notFound, reason: "Session not found")
        }
        session.title = updateDTO.title ?? session.title
        session.mp4URL = updateDTO.mp4URL ?? session.mp4URL
        session.hlsURL = updateDTO.hlsURL ?? session.hlsURL
        session.updatedAt = Date()
        session.publishedAt = updateDTO.publishedAt ?? session.publishedAt
        session.price = updateDTO.price ?? session.price
        session.course = updateDTO.course ?? session.course
        session.article = updateDTO.article ?? session.article
        session.status = updateDTO.status ?? session.status
        try await session.save(on: req.db)
        return session
    }
    
    static func delete(_ req: Request, object: String) async throws -> Vapor.HTTPStatus {
        let uuid = try await getIDFromSlug(req, slug: object)
        guard
            let session = try await SessionModel.find(uuid, on: req.db)
        else {
            throw Abort(.notFound, reason: "Session not found")
        }
        try await session.delete(on: req.db)
        return .noContent
    }
}

extension SessionService: BackendContentFilterProtocol {
    static func getByStatus(_ req: Vapor.Request, status: StatusEnum.RawValue) async throws -> [SessionModel] {
        let session = try await SessionModel.query(on: req.db)
            .filter(\.$status == status)
            .all()
        return session
    }
    
    static func search(_ req: Vapor.Request, query: String) async throws -> [SessionModel] {
        let session = try await SessionModel.query(on: req.db)
            .group(.or) { or in
                or.filter(\.$title == query)
            }.all()
        return session
    }
}

extension SessionService: GetSelectedObjectProtocol {
    typealias model = SessionModel
    
    typealias request = Vapor.Request
    
    static func getSelectionObject(_ req: Request, object: String) async throws -> SessionModel {
        let user = req.auth.get(UserModel.self)
        guard
            let session = try await SessionModel.query(on: req.db)
                .filter(\.$slug == object)
                .filter(\.$status == StatusEnum.published.rawValue)
                .first()
        else {
            throw Abort(.notFound)
        }
        return session
    }
}

extension SessionService: TransformProtocol {
    typealias answerWithID = UUID
    
    static func getIDFromSlug(_ req: Vapor.Request, slug: String) async throws -> UUID {
        guard
        let session = try await SessionModel.query(on: req.db)
            .filter(\.$slug == slug)
            .first() else {
                throw Abort(.notFound)
            }
        return session.id!
    }
}
